#include "StdAfx.h"
#include "ManagedImageConverter.h"
//////////////////////////////////////////////////////////////////////////
NAMESPACE_BEGIN_UI
//////////////////////////////////////////////////////////////////////////
array<Color>^ ManagedImageConverter::CreateGray8DepthPalette()
{
	array<Color>^ palette = gcnew array<Color>(256);
	for (int i = 0; i < palette->Length; i++)
	{
		palette[i] = Color::FromArgb(i, i, i);
	}
	return palette;
}

array<Color>^ ManagedImageConverter::CreateGray16DepthPalette()
{
	array<Color>^ palette = gcnew array<Color>(65536);
	for (int i = 0; i < palette->Length; i++)
	{
		palette[i] = Color::FromArgb(i / 0xFF, i / 0xFF, i / 0xFF);
	}
	return palette;
}

void ManagedImageConverter::FillBitmapData8U1C(BitmapData^ data, void* pLine0, int stride, Color* palette)
{
	int width = data->Width;
	int height = data->Height;
	int sourceStride = stride;
	int targetStride = data->Stride;
	unsigned char* pSource = (unsigned char*)pLine0;
	unsigned long* pTarget = (unsigned long*)data->Scan0.ToPointer();
	for (int row = 0; row < height; row++)
	{
		unsigned char* pSourceLine = (unsigned char*)((unsigned char*)pSource + row * sourceStride);
		unsigned long* pTargetLine = (unsigned long*)((unsigned char*)pTarget + row * targetStride);
		for (int col = 0; col < width; col++)
		{
			pTargetLine[col] = palette[pSourceLine[col]].ToArgb();
		}
	}
}

void ManagedImageConverter::FillBitmapData16U1C(BitmapData^ data, void* pLine0, int stride, Color* palette)
{
	int width = data->Width;
	int height = data->Height;
	int sourceStride = stride;
	int targetStride = data->Stride;
	unsigned short* pSource = (unsigned short*)pLine0;
	unsigned long* pTarget = (unsigned long*)data->Scan0.ToPointer();
	for (int row = 0; row < height; row++)
	{
		unsigned short* pSourceLine = (unsigned short*)((unsigned char*)pSource + row * sourceStride);
		unsigned long* pTargetLine = (unsigned long*)((unsigned char*)pTarget + row * targetStride);
		for (int col = 0; col < width; col++)
		{
			pTargetLine[col] = palette[pSourceLine[col]].ToArgb();
		}
	}
}

void ManagedImageConverter::FillBitmapData8U3C(BitmapData^ data, void* pLine0, int stride)
{
	int width = data->Width;
	int height = data->Height;
	int sourceStride = stride;
	int targetStride = data->Stride;
	unsigned char* pSource = (unsigned char*)pLine0;
	unsigned long* pTarget = (unsigned long*)data->Scan0.ToPointer();
	for (int row = 0; row < height; row++)
	{
		unsigned char* pSourceLine = (unsigned char*)((unsigned char*)pSource + row * sourceStride);
		unsigned char* pTargetLine = (unsigned char*)((unsigned char*)pTarget + row * targetStride);
		for (int col = 0; col < width; col++)
		{
			pTargetLine[4 * col + 0] = pSourceLine[3 * col + 0];
			pTargetLine[4 * col + 1] = pSourceLine[3 * col + 1];
			pTargetLine[4 * col + 2] = pSourceLine[3 * col + 2];
			pTargetLine[4 * col + 3] = 0xFF;
		}
	}
}

void ManagedImageConverter::FillBitmapData8U4C(BitmapData^ data, void* pLine0, int stride)
{
	int width = data->Width;
	int height = data->Height;
	int sourceStride = stride;
	int targetStride = data->Stride;
	unsigned long* pSource = (unsigned long*)pLine0;
	unsigned long* pTarget = (unsigned long*)data->Scan0.ToPointer();
	for (int row = 0; row < height; row++)
	{
		unsigned long* pSourceLine = (unsigned long*)((unsigned char*)pSource + row * sourceStride);
		unsigned long* pTargetLine = (unsigned long*)((unsigned char*)pTarget + row * targetStride);
		for (int col = 0; col < width; col++)
		{
			pTargetLine[col] = pSourceLine[col];
		}
	}
}

Bitmap^ ManagedImageConverter::BuildBitmapFromCache(ManagedImage^ img, Bitmap^ cache)
{
	Bitmap^ bitmap = cache;
	if (bitmap == nullptr || bitmap->PixelFormat != PixelFormat::Format32bppArgb || 
		bitmap->Width != img->Width || bitmap->Height != img->Height)
	{
		bitmap = gcnew Bitmap(img->Width, img->Height, PixelFormat::Format32bppArgb);
	}
	return bitmap;
}

Bitmap^ ManagedImageConverter::BuildBitmapFromCache(ManagedMat^ img, Bitmap^ cache)
{
	Bitmap^ bitmap = cache;
	if (bitmap == nullptr || bitmap->PixelFormat != PixelFormat::Format32bppArgb || 
		bitmap->Width != img->Width || bitmap->Height != img->Height)
	{
		bitmap = gcnew Bitmap(img->Width, img->Height, PixelFormat::Format32bppArgb);
	}
	return bitmap;
}

bool ManagedImageConverter::CheckPixelFormatIsSupport(ManagedImage^ img)
{
	if ((img->Channels == 1 && (img->Depth == PixelDepth::Depth8U || img->Depth == PixelDepth::Depth16U)) ||
		((img->Channels == 3 || img->Channels == 4) && img->Depth == PixelDepth::Depth8U))
	{
		return true;
	}
	return false;
}

bool ManagedImageConverter::CheckPixelFormatIsSupport(ManagedMat^ img)
{
	if (img->Type == MatFlag::CV8UC1 || img->Type == MatFlag::CV8UC3 || img->Type == MatFlag::CV8UC4 || img->Type == MatFlag::CV16UC1)
	{
		return true;
	}
	return false;
}

Bitmap^ ManagedImageConverter::Convert(ManagedArr^ img, Bitmap^ cache, array<Color>^ palette)
{
	if (img == nullptr)
	{
		return nullptr;
	}
	if (img->GetType() == ManagedImage::typeid || img->GetType()->IsSubclassOf(ManagedImage::typeid))
	{
		ManagedImage^ image = safe_cast<ManagedImage^>(img);
		if (!CheckPixelFormatIsSupport(image))
		{
			throw gcnew System::NotSupportedException("Not support pixel format");
		}

		if (image->Depth == PixelDepth::Depth8U && image->Channels == 1 && (palette == nullptr || palette->Length != 0xFF))
		{
			palette = CreateGray8DepthPalette();
		}
		else if(image->Depth == PixelDepth::Depth16U && image->Channels == 1 && (palette == nullptr || palette->Length != 0xFFFF))
		{
			palette = CreateGray16DepthPalette();
		}

		Bitmap^ bitmap = BuildBitmapFromCache(image, cache);
		// Lock the bitmap's bits.  
		Rectangle rect = Rectangle(0, 0, bitmap->Width, bitmap->Height);
		BitmapData^ data = bitmap->LockBits(rect, ImageLockMode::WriteOnly, PixelFormat::Format32bppArgb);
		unsigned char* pSource = (unsigned char*)image->Scan0.ToPointer();
		int sourceStride = image->Stride;
		if (image->Origin == ImageOrigin::BottomLeft)
		{
			pSource = pSource + (image->Height - 1) * sourceStride;
			sourceStride = -sourceStride;
		}

		if (image->Channels == 1 && image->Depth == PixelDepth::Depth8U)
		{
			pin_ptr<Color> pPalette = &palette[0];
			FillBitmapData8U1C(data, pSource, sourceStride, pPalette);
		}
		else if (image->Channels == 1 && image->Depth == PixelDepth::Depth16U)
		{
			pin_ptr<Color> pPalette = &palette[0];
			FillBitmapData16U1C(data, pSource, sourceStride, pPalette);
		}
		else if (image->Channels == 3 && image->Depth == PixelDepth::Depth8U)
		{
			FillBitmapData8U3C(data, pSource, sourceStride);
		}
		else if (image->Channels == 4 && image->Depth == PixelDepth::Depth8U)
		{
			FillBitmapData8U4C(data, pSource, sourceStride);
		}
		bitmap->UnlockBits(data);
		return bitmap;
	}
	else if (img->GetType() == ManagedMat::typeid || img->GetType()->IsSubclassOf(ManagedMat::typeid))
	{
		ManagedMat^ mat = safe_cast<ManagedMat^>(img);
		if (!CheckPixelFormatIsSupport(mat))
		{
			throw gcnew System::NotSupportedException("Not support pixel format");
		}
		if (mat->Type == MatFlag::CV8UC1 && (palette == nullptr || palette->Length != 0xFF))
		{
			palette = CreateGray8DepthPalette();
		}
		else if (mat->Type == MatFlag::CV16UC1 && (palette == nullptr || palette->Length != 0xFFFF))
		{
			palette = CreateGray16DepthPalette();
		}

		Bitmap^ bitmap = BuildBitmapFromCache(mat, cache);
		// Lock the bitmap's bits.  
		Rectangle rect = Rectangle(0, 0, bitmap->Width, bitmap->Height);
		BitmapData^ data = bitmap->LockBits(rect, ImageLockMode::WriteOnly, PixelFormat::Format32bppArgb);
		unsigned char* pSource = (unsigned char*)mat->Scan0.ToPointer();
		int sourceStride = mat->Stride;
		if (mat->Type == MatFlag::CV8UC1)
		{
			pin_ptr<Color> pPalette = &palette[0];
			FillBitmapData8U1C(data, pSource, sourceStride, pPalette);
		}
		else if (mat->Type == MatFlag::CV16UC1)
		{
			pin_ptr<Color> pPalette = &palette[0];
			FillBitmapData16U1C(data, pSource, sourceStride, pPalette);
		}
		else if (mat->Type == MatFlag::CV8UC3)
		{
			FillBitmapData8U3C(data, pSource, sourceStride);
		}
		else if (mat->Type == MatFlag::CV8UC4)
		{
			FillBitmapData8U4C(data, pSource, sourceStride);
		}
		bitmap->UnlockBits(data);
		return bitmap;
	}
	else
	{
		throw gcnew System::NotSupportedException();
	}
}

ManagedImage^ ManagedImageConverter::Convert(Bitmap^ bitmap, ManagedImage^ parameter)
{
	throw gcnew System::NotImplementedException();
}

System::Object^ ManagedImageConverter::Convert(System::Object^ value, System::Type^ targetType, System::Object^ parameter, CultureInfo^ culture)
{
	if (value->GetType()->IsSubclassOf(ManagedArr::typeid) && targetType == Bitmap::typeid)
	{
		ManagedArr^ img = safe_cast<ManagedArr^>(value);
		Bitmap^ cache = nullptr;
		array<Color>^ palette = nullptr;
		return Convert(img, cache, palette);
	}
	return nullptr;
}

System::Object^ ManagedImageConverter::ConvertBack(System::Object^ value, System::Type^ targetType, System::Object^ parameter, CultureInfo^ culture)
{
	throw gcnew System::NotImplementedException();
}

//////////////////////////////////////////////////////////////////////////
NAMESPACE_END_UI
//////////////////////////////////////////////////////////////////////////